/**
 * Copyright 2012 Roger Cockshutt
 * krakenbrau@gmail.com
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.googlecode.parallelrl.process;

import com.googlecode.blacken.terminal.BlackenKeys;
import com.googlecode.parallelrl.Config;
import com.googlecode.parallelrl.ParallelRL;
import com.googlecode.parallelrl.data.LevelProvider;
import com.googlecode.parallelrl.data.ecs.ECSManager;
import com.googlecode.parallelrl.display.Colors;
import com.googlecode.parallelrl.display.GameLogger;
import com.googlecode.parallelrl.display.Mapper;
import com.googlecode.parallelrl.display.Menu;
import com.googlecode.parallelrl.display.Screen;
import com.googlecode.parallelrl.display.StatusReadout;
import com.googlecode.parallelrl.util.Message;
import com.googlecode.parallelrl.util.Point2D;

/**
 * @author Roger Cockshutt
 *
 * 
 */
public class Game implements Runnable {
	
	//private static Integer activeControllableEntity_;
	
	public static boolean running = false;
	public static boolean restart = true;
	private static Long lastCycleTime = System.nanoTime();
	private static Long firstCycleTime = lastCycleTime;
	public static Long numCycles = 0L;
	public static Double currentFPS = 0D;
	public static Double averageFPS = 0D;
	public static Double lastCycleDuration = 0D;
	public static Double averageCycleDuration = 0D;
	
	public Game() {
		initialize();
	}
	
	public static void initialize() {
		if (ParallelRL.doDebug)
			System.out.println("Clearing events...");
		EventSystem.clear();
		if (ParallelRL.doDebug)
			System.out.println("Clearing entities...");
		ECSManager.killAll();
		if (ParallelRL.doDebug)
			System.out.println("Clearing levels...");
		LevelProvider.initialize();
		if (ParallelRL.doDebug)
			System.out.println("Resetting time...");
		
		currentStep = 0;
		running = true;
	}
	
	public static long currentStep = 0;
	
	/**
	 * Game loop.
	 */
	@Override
	public void run() {
		
		showTitle();
		
		if (LevelProvider.getCurrentLevel() == null) {
			LevelProvider.getNewLevel(Config.defaultLevelSize);
			if (ECSManager.getAssemblage("PLAYER") != null) {
				Long player = ECSManager.createEntityFromAssemblage("PLAYER");
				LevelProvider.getCurrentLevel().addEntityAt(
						player, new Point2D(1,1));
				EventSystem.newEvent(1L, player, "turn", (String)null);
				BehaviourManager.setLastActiveControllable(player);
				BehaviourManager.calculateSecondaries(player);
			}
		}
		
		// Game loop.
		while (running) {
			currentStep = EventSystem.getNextStep();
			EventSystem.resolveCurrentEvents();
		}
		
	}
	
	private static void doFPS() {
		if (Config.fpsLimit != null
				&& Config.fpsLimit > 0) {
			while (System.nanoTime() < lastCycleTime + (1000000000 / Config.fpsLimit));
		}
		Long currentTime = System.nanoTime();
		Long elapsedTimeSinceLast = Math.abs(currentTime - lastCycleTime);
		Long elapsedTimeSinceFirst = Math.abs(currentTime - firstCycleTime);
		currentFPS = (1D / elapsedTimeSinceLast) * 1000000000D;
		averageFPS = (numCycles * 1000000000D) / elapsedTimeSinceFirst;
		lastCycleDuration = elapsedTimeSinceLast / 1000000D;
		averageCycleDuration = elapsedTimeSinceFirst / numCycles / 1000000D;
		lastCycleTime = currentTime;
	}
	
	public static void updateAll() {
		Screen.get().clear();
		Mapper.get().update();
		GameLogger.get().update();
		StatusReadout.get().update();
		Screen.get().refresh();
		numCycles++;
		doFPS();
	}
	
	/**
	 * Wait for a key, and discard it.
	 */
	public static void waitForKey() {
		int ch = BlackenKeys.NO_KEY;
		while(ch == BlackenKeys.NO_KEY || ch == BlackenKeys.RESIZE_EVENT) {
			ch = Screen.get().getch();
		}
	}
	
	private static void showTitle() {
		Menu titleMenu = new Menu("ParallelRL", Colors.libTCOD.blue.normal);
		Message playOption = new Message("Arena/Testing mode", Colors.libTCOD.blue.lighter);
		Message loadOption = new Message("Load saved arena", Colors.libTCOD.blue.lighter);
		Message quitOption = new Message("Quit", Colors.libTCOD.blue.lighter);
		titleMenu.addOptions(playOption, loadOption, quitOption);
		String choice = null;
		while (choice == null) {
			choice = titleMenu.show();
		}
		switch (choice) {
		case "Arena/Testing mode":
			return;
		case "Load saved arena":
			LevelProvider.load();
			return;
		case "Quit":
			running = false;
			restart = false;
			ParallelRL.doDebug = false;
			break;
		default:
			// Should never get here
		}
	}
	
//	public static Integer getActiveControllableEntity() {
//		List<Integer> players = ECSManager.getAllEntitiesPossessingComponent("PlayerNature");
//		if (players.isEmpty()) {
//			Game._running = false;
//			Game._restart = true;
//			return null;
//		}
//		return players.get(0);
//	}
	
	public static void addStep(Integer time) {
		currentStep += time;
	}

}
